home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-02-18 | 56.7 KB | 1,830 lines |
- Newsgroups: comp.sources.unix
- From: brnstnd@nyu.edu (Dan Bernstein)
- Subject: v25i131: Generalized interface to pseudo-tty devices, Part05/09
- Sender: unix-sources-moderator@pa.dec.com
- Approved: vixie@pa.dec.com
-
- Submitted-By: brnstnd@nyu.edu (Dan Bernstein)
- Posting-Number: Volume 25, Issue 131
- Archive-Name: pty4/part05
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 5 (of 9)."
- # Contents: NEW QUESTIONS ULOGS.draft1 pty.1 ptysecure.c ptysigler.c
- # timer.c
- # Wrapped by vixie@cognition.pa.dec.com on Wed Feb 19 13:35:04 1992
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'NEW' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'NEW'\"
- else
- echo shar: Extracting \"'NEW'\" \(9164 characters\)
- sed "s/^X//" >'NEW' <<'END_OF_FILE'
- pty 4.0 (``newpty'' below) is an almost complete rewrite of pty 3.0
- X(``oldpty''). Some features and misfeatures have been removed:
- X
- X1. newpty no longer allows for systems without file descriptor passing.
- X2. newpty does not go to obscene lengths to ensure non-blocking I/O.
- X3. newpty does not worry about the buggy MAXUPRC handling in BSD 4.{2,3}.
- X4. newpty no longer maintains an entire hierarchy of special files.
- X5. The x* utilities and -xS are gone.
- X6. For security reasons, -f is gone.
- X7. sessuser is gone, as nobody uses it. In its place is sessmenu (see below).
- X8. The semi-automatic INSTALL has been replaced by a sane configuration system.
- X
- Several new features have been added:
- X
- X9. You can now disconnect, reconnect, and rename sessions remotely.
- X10. newpty performs much better tests than oldpty for tty security.
- X11. newpty uses my sigsched library; this greatly simplifies the code.
- X12. newpty's random tty searching is not vulnerable to secondary clustering.
- X13. newpty supports session and session-connection logs as well as [uw]tmp.
- X14. newpty now maintains a single, reliable communications file for each pty.
- X15. Internally, the master and signaller are much more cleanly separated.
- X16. oldpty's -d served two different functions. newpty's -d is simpler.
- X17. When oldpty reaches EOF on input, it loops forever. newpty doesn't.
- X18. newpty's error messages are, believe it or not, comprehensible.
- X19. newpty lets the user set his host field in utmp (this is configurable).
- X20. newpty gives the slave a PTY variable showing its extension (e.g., p7).
- X21. A ``sessmenu'' utility can serve as a friendly session-aware login wrapper.
- X22. newpty includes ``utmpinit'' to properly initialize /etc/utmp.
- X23. A ``nobuf'' utility now provides a completely transparent channel.
- X
- X
- XFurther notes on these:
- X
- X1. Large sections of code in oldpty were dedicated to handling systems
- without reliable file descriptor passing (i.e., many BSD 4.2 variants).
- In newpty this code is gone. I still work around the fd passing bugs
- that appear in the latest SunOS and Ultrix releases.
- X
- X2. In oldpty, I spent a lot of time on non-blocking I/O. There's a long
- and sad story behind this; read on.
- X
- XEven though every bit of semantics of read() and write() is drastically
- affected by whether the descriptor is non-blocking, UNIX implementors
- through the years have always---I mean *always*---provided for
- non-blocking I/O on a given open file, rather than on a particular
- descriptor. This is fine for programs which open their own descriptors.
- But pty has to do I/O on input and output descriptors which someone else
- has set up. It can't set NDELAY on those descriptors, because another
- innocent program might be reading or writing the same descriptors, and
- NDELAY will in all probability make that program go into an infinite
- loop!
- X
- Of course, what most people do is just select() for readability or
- writability, then assume the operations won't block. The problem is that
- they might block. By the time pty gets around to reading, someone else
- could have read the input which caused select() to return. Similarly,
- any write of N bytes to a pipe with M bytes free, with N > M > 0, will
- block, even though select will return true. So I figured out a way to
- achieve true per-process non-blocking I/O: simply arrange for SIGALRM to
- be sent every fraction of a second, interrupting any blocking read() or
- write(). This works---but not under BSD 4.2, which restarts system
- calls. oldpty's configuration process reflected this extra complexity.
- X
- Now I've seen the light. True non-blocking I/O be damned. I'm just going
- to select() and do my operations like everyone else. It's not my fault
- that so few implementors understand why non-blocking I/O on an entire
- open file, rather than a particular descriptor, is so useless. If emacs
- and screen and so many other programs don't care, I won't either. newpty
- takes none of oldpty's precautions, and if it ever blocks because a pipe
- is too full or because there are multiple readers, don't blame me. Blame
- a truly senseless OS design decision.
- X
- X3. All BSD variants have a maximum process limit, MAXUPRC or maxuprc in
- X<sys/param.h>. When there are MAXUPRC processes with some uid (other
- than 0), the next fork() by that uid will fail. A bit of thought will
- convince you that MAXUPRC should apply to the real uid. If it applied to
- the effective uid, then it would affect execve() of setuid programs.
- XEven worse, if you had a program setuid (say) ingres, and MAXUPRC users
- ran that program at once, no other users would be able to run it! So
- applying MAXUPRC to the effective uid is a really bad idea.
- X
- Guess what? Almost all vendors have applied MAXUPRC to the effective
- uid. On the other hand, few of them have paid any attention to execve()
- of setuid programs, so in the ingres case any number of users can run
- the program, dragging the process count way above MAXUPRC. If you have
- any experience in crashing UNIX systems you'll guess what I'm about to
- say: If the process count for a uid does go above MAXUPRC, then as soon
- as MAXUPRC of those processes die, the system will panic. (This is true
- under, e.g., Ultrix.) Brilliant.
- X
- As you can imagine, none of this is conducive to safe setuid
- programming. System panics aside, pty has to fork twice, and I didn't
- want to limit it to MAXUPRC/2 simultaneous invocations. So oldpty swaps
- its uid and euid around each fork.
- X
- This introduces its own set of problems. First, the moment a setuid
- program does setreuid(geteuid(),getuid()), it opens itself up to ptraces
- by the (original) real uid. Then all security is lost. Second, this
- swapping is utterly pointless on systems with a sane MAXUPRC based on
- real uids. Finally, I haven't seen anyone else even considering this
- issue, let alone implementing workarounds.
- X
- So once again I've joined the crowd. newpty pretends that process limits
- don't exist. If it fails miserably, or your system crashes, just because
- too many users run setuid programs at once, blame an OS design decision
- as senseless as the lack of per-descriptor non-blocking I/O.
- X
- X5. The x* utilities and -xS are gone because it was easier in practice
- for unprivileged users to install their own copies of pty rather than
- use the system copy in some weird mode.
- X
- X10. With the default settings, oldpty would detect when someone else had
- the tty open. (Essentially the same test appeared several months later
- in a ``critical'' telnetd/rlogind security patch from Sun.) There were
- two problems: first, in a common case, pty would end up dying instead
- of simply trying another tty; second, the test can be defeated without
- too much trouble (though after these years I'm not sure there are more
- than a few dozen people in the world who know how to exploit this).
- X
- pty 4.0 uses a much more reliable test---it's now more secure than the
- tests used by Sun, Convex, DEC, and several other vendors. See the
- SECURITY file for further details.
- X
- X11. Internally, pty 4.0 now uses my signal-schedule (aka non-preemptive
- threads) library for multitasking. This makes the master much, much
- easier to understand, stops certain race conditions, and removes any
- need for explicit variable locks (like flagqwinch in oldpty).
- X
- X12. oldpty's pseudo-random tty searching started from a random spot, but
- searched in a fixed order from that spot. This left it open to secondary
- clustering. In newpty I added (in effect) a secondary hash function, and
- since tertiary clustering simply doesn't happen in practice, this
- problem should be gone.
- X
- X13. newpty supports the session and session-connection logs described in
- my pty paper. I fervently hope that these replace utmp and wtmp, if only
- because they have enough space to store full host information!
- X
- X14. In oldpty, the master process would create a communications file
- only while it was disconnected, then remove the file when it was
- reconnected. newpty maintains the communications file all the time.
- This eliminates some deep races and lets the master maintain certain
- information instead of storing it in a file for other processes to read.
- X
- X15. The roles of the master and signaller are now much more clearly
- separated. The master does not know whether the signaller is detached,
- or is manipulating a tty, or supports job control. The master does not
- attempt to associate itself with the signaller's tty on reconnect.
- Instead it always remains associated with the slave's tty.
- X
- X16. In oldpty, pty -d (detached) combined two functions. One had to do
- with the (obsolete) kernel concept of a controlling tty. The other told
- pty that it was starting under a tty and should pay attention to that
- tty's modes in constructing modes for the pseudo-tty. In newpty, -d
- controls only the second function. pty will automatically handle the
- kernel's controlling ttys correctly in all situations.
- X
- X17. newpty now treats true EOF on input as (1) a disconnect, if it's a
- session; (2) forcing -R, so that it won't read further input, if it's
- not a session. This is a really tricky issue---ever since UNIX supported
- ttys it's had two different concepts of EOF, and every program that
- handles ttys has to deal with both. If you have any better suggestions,
- let me know.
- END_OF_FILE
- if test 9164 -ne `wc -c <'NEW'`; then
- echo shar: \"'NEW'\" unpacked with wrong size!
- fi
- # end of 'NEW'
- fi
- if test -f 'QUESTIONS' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'QUESTIONS'\"
- else
- echo shar: Extracting \"'QUESTIONS'\" \(8173 characters\)
- sed "s/^X//" >'QUESTIONS' <<'END_OF_FILE'
- I selected the questions below from articles posted to the USENET
- newsgroup comp.unix.questions. All the questions are reasonably easy to
- answer with pty and not too easy to answer with other widely available
- tools. So I hope this file is useful, and I hope other software authors
- adopt the QUESTIONS idea.
- X
- X
- X
- X1. How do I redirect telnet's input?
- X
- Answer: Run pty telnet instead of telnet.
- X
- Long answer: Be careful---as soon as telnet successfully connects to the
- remote host, it flushes its input. So if you do something like
- X
- X (echo help; echo quit) | pty telnet whatever 25
- X
- telnet will read ``help'' and ``quit'', then after a second or two
- connect to the host, then flush the input. It won't get any input after
- that. An easy solution is to delay the input a few seconds:
- X
- X (sleep 5; echo help; echo quit) | pty telnet whatever 25
- X
- X(Try it!) For a more robust solution, see question #4 below.
- X
- Why this was a problem: UNIX beginners learn that they can feed input to
- programs with redirection or with pipes. They're often surprised to see
- that this doesn't work:
- X
- X (echo help; echo quit) | telnet whatever 25
- X
- telnet, like many other utilities, considers itself an ``interactive''
- program. It assumes that it's talking to a user's terminal directly, so
- that it can put the terminal into character-at-a-time mode. If you
- redirect its input, it won't be able to find the terminal, and it'll
- complain or do weird things. Similar comments apply to editors, like vi.
- X
- Why the answer solves the problem: pty creates a pseudo-terminal---
- something which looks like a normal terminal but is under pty's control.
- pty runs telnet under the pseudo-terminal and forwards all input and
- output. Now telnet works, because it sees the pseudo-terminal and thinks
- it's talking to the user.
- X
- X
- X
- X2. How do I redirect passwd's input?
- X
- Answer: pty passwd.
- X
- Long answer: Like telnet, passwd flushes its input, so you probably want
- to do something like
- X
- X (sleep 5; echo oldpassword; sleep 5; echo newpassword; \
- X sleep 5; echo newpassword) | pty passwd
- X
- A better solution appears in #4 below. If you're using passwd as a
- system administrator to change somebody else's password, you want
- X
- X (sleep 5; echo password; sleep 5; echo password) | pty passwd shmoe
- X
- I certainly don't recommend keeping passwords visible in shell scripts,
- but if you ever need to do this, you can. In single-user mode this is
- reasonably safe.
- X
- Why this was a problem: passwd is even more insistent than telnet on
- talking to a user directly. So it opens /dev/tty for its input. Some
- other programs open /dev/tty for both input and output. This avoids any
- redirection you might have put into place.
- X
- Why the answer solves the problem: As before, pty creates a
- pseudo-terminal for passwd to talk to. When passwd opens /dev/tty, it
- gets the pseudo-terminal. All the redirection is outside pty, safe from
- passwd's tampering.
- X
- X
- X
- X3. Why doesn't tr '[A-Z]' '[a-z]' | cat -t print anything I type?
- X
- Answer: Do nobuf tr '[A-Z]' '[a-z]' | cat -t.
- X
- Long answer: You can use pty -0 (also known as condom, or, if you're not
- in such a picturesque mood, as ttyprotect) in place of nobuf, but nobuf
- invokes pty carefully so that EOF on the input will be passed through to
- tr. The effect of this pipeline is to change all uppercase letters into
- lowercase, then print control characters visibly (e.g., ^H instead of
- backspace).
- X
- Why this was a problem: The original pipeline, without nobuf, doesn't
- seem to produce any output. What happens is that tr says ``Oh, I say.
- My output is going into a pipe! I guess there's no point in sending
- output as fast as possible. So I'll build up a buffer of output, say up
- to 8192 characters, before I send any of it.'' In fact, any program
- which uses stdio will do the same thing. The problem is that the guess
- is wrong---you don't want 8192 characters held inside tr. You want each
- line sent through without any buffering.
- X
- Why the answer solves the problem: Once again, nobuf (really pty)
- creates a pseudo-terminal and runs tr inside it. Then tr (really stdio)
- says ``Oh, I say. My output is going into a terminal! I'll bet there's a
- user watching every word. So I'll send each line as soon as I'm done
- with it.'' And it does.
- X
- X
- X
- X4. How do I start a program, respond to its prompts, give the correct
- X replies, and catch the output---all from a script?
- X
- If you're good at shell programming, you might have already figured out
- most of this. Let's say you're on a machine (like a Sun) which can
- create named pipes with mknod foo p. You might try to automate a telnet
- session, keeping a record in a file, like this:
- X
- X #!/bin/sh
- X (umask 077; mknod input p; mknod output p)
- X telnet foo < input | tee record > output &
- X exec 4>input 5<output
- X waitfor login: <&5 2>/dev/null
- X echo 'username' >&4
- X waitfor Password: <&5 2>/dev/null
- X echo 'password' >&4
- X # etc.
- X
- X(waitfor reads its input, character by character, until its first
- argument matches the most recent characters as a literal string.)
- X
- This almost works. sh can handle this sort of automation without
- trouble. The only problem is that telnet is interactive. That's where
- pty comes in. This works:
- X
- X #!/bin/sh
- X (umask 077; mknod input p; mknod output p)
- X pty -0 telnet foo < input | nobuf tee record > output &
- X exec 4>input 5<output
- X waitfor login: <&5 2>/dev/null
- X echo 'username' >&4
- X waitfor Password: <&5 2>/dev/null
- X echo 'password' >&4
- X # etc.
- X
- On machines without named pipes, you'll have to create (unnamed) pipes
- in C. But whatever method of automation you use, pty will let you apply
- that method to an interactive program.
- X
- X
- X
- X5. How do I get rn to process KILL files in the background?
- X
- Answer:
- X
- X % sess -R sh -c 'sessname; rn' &
- X [1] 20417 < the shell prints this >
- X session pf < this comes from sessname >
- X < rn produces output in the background, and you can do something else. >
- X < ... >
- X < when you want to put it back into the foreground: >
- X % %1
- X sess -R sh -c 'sessname; rn' < the shell prints this >
- X ^C
- X % sess reconnect pf
- X < now it's just as if rn had started in the foreground. >
- X
- sess is an abbreviation for ``pty -s''. It starts a _session_ which you
- can disconnect and reconnect. Later on, when you put it into the
- background and type ^C, it stays disconnected in the background. Then
- you can start a new session and reconnect to it.
- X
- X-R means ``don't read.'' It's a lot like rsh -n. It tells pty not to
- read anything from the keyboard. When you start ``sess reconnect pf'',
- you don't specify -R, so pty does take keyboard input.
- X
- Why this was a problem: rn is yet another ``interactive'' program. It
- insists on being in the foreground before it does anything else.
- Unfortunately, after you put it into the foreground, it might spend ten
- minutes processing KILL files!
- X
- Why the answer solves the problem: Once again, pty creates a
- pseudo-terminal for rn. Under that pseudo-terminal, rn is in the
- foreground, so it will happily process KILL files.
- X
- X
- X6. How do I get the terminal speed from a shell script?
- X
- Answer: Under sh, speed="`pty stty speed`".
- X
- Why this was a problem: When you type ``stty speed'', most versions of
- stty will look for the terminal on stdout, get the speed of that
- terminal, and print the result on stderr. So if you try
- X
- X speed="`stty speed`"
- X
- from sh, two things go wrong. The first is that sh is reading stty's
- output, so when stty looks for the terminal, it'll find only a pipe. The
- second is that when stty prints the result, it goes to stderr, which sh
- isn't paying attention to.
- X
- Other versions of stty look for the terminal on stdin or stderr, and
- print the result to stdout. So even if you kludge around the first
- problem by moving file descriptors around, chances are your code will
- mysteriously blow up on the next machine.
- X
- Why the answer solves the problem: pty creates a pseudo-terminal for
- stty, with exactly the same characteristics (including speed) as the
- original tty. It doesn't matter whether stty looks at stdin or stdout or
- stderr. It'll just see the pseudo-tty. It also doesn't matter whether
- stty prints its result to stdout or stderr. pty will collect both
- results into its stdout for the shell.
- END_OF_FILE
- if test 8173 -ne `wc -c <'QUESTIONS'`; then
- echo shar: \"'QUESTIONS'\" unpacked with wrong size!
- fi
- # end of 'QUESTIONS'
- fi
- if test -f 'ULOGS.draft1' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'ULOGS.draft1'\"
- else
- echo shar: Extracting \"'ULOGS.draft1'\" \(6271 characters\)
- sed "s/^X//" >'ULOGS.draft1' <<'END_OF_FILE'
- An analysis of user login records
- Daniel J. Bernstein
- draft 1
- X10/5/91
- X
- X
- X1. BSD login records
- X
- On a BSD UNIX system, three files keep track of user logins. /etc/utmp
- records which users are on which lines, and when they logged in (or, for
- unused lines, logged out). To put it differently, utmp records the most
- recent start or stop time of the latest session on each line, and which
- user owns or owned that session. utmp also records the first 16
- characters of the remote hostname for any network connection.
- X
- A parallel file /usr/adm/wtmp records all changes to utmp. The last wtmp
- entry for line xx is the current utmp entry for line xx. wtmp also uses
- special codes to indicate reboots and other unusual events. Typically
- wtmp is ``rotated'' every month: /usr/adm/wtmp is cleared, with a copy
- of the old information saved in /usr/adm/wtmp.0. Any previous wtmp.0 is
- saved in wtmp.1, and so on up to wtmp.7, which is thrown away.
- X
- The third file is /usr/adm/lastlog, a flat database indexed by uid
- showing the most recent login entry for each user. lastlog and utmp have
- different formats but carry essentially the same information.
- X
- X
- X2. Problems
- X
- utmp provides only 16 characters for hostnames which are often much too
- long to fit. Once utmp (and wtmp and lastlog) truncate a name, any extra
- characters are lost permanently. Other information---the remote TCP
- port, the remote user as seen by rlogind or via RFC 931, etc.---isn't
- recorded at all. This makes it very difficult to trace network attacks.
- X
- XFurthermore, when users take advantage of session management (as
- described in []), utmp and wtmp lose even more information. A user may
- start a session at work, disconnect it without logging out, go home, and
- reconnect to the session from an entirely different location. It makes
- sense to talk about the start and stop time of the session, each connect
- and disconnect time, and each connect location. utmp and wtmp record
- none of this.
- X
- Another problem with utmp is that it has never been clear whether utmp
- should record all connections (see []) or only interactive connections.
- Given that the programs which depend most heavily on utmp---to wit, user
- communication programs such as ``talk'' and ``write''---should only see
- interactive connections, it makes sense to omit windows and subshells,
- but then window information is lost.
- X
- lastlog is mainly an optimization: login and finger both report the last
- login time for a particular user, and it would be wasteful to search
- through wtmp each time. But lastlog only keeps track of the very latest
- login, with no indication of any previous logins. It does not record
- logout time. Even worse, users and administrators cannot find out when
- their accounts are being attacked, because lastlog only records
- successful logins.
- X
- X
- X3. Solutions
- X
- One ``solution'' is to add more and more fields to utmp, recording more
- and more information. But this is the wrong strategy. Consider the
- parallel wtmp file. If utmp has (for instance) fields for the latest
- connection, then every time a user connects or disconnects, the basic
- session fields will be repeated in wtmp for no good reason. This wastes
- disk space but also indicates a fundamental failure in the model.
- X
- The right solution is to give each file a specific purpose. utmp should
- only keep track of sessions; the host field should be removed. Complete
- connection information, including connect and disconnect times, remote
- host as both IP address and name, remote port, and remote user if known,
- can go into a separate file.
- X
- To solve the problem of interactive versus non-interactive sessions,
- utmp should be split in two. The original utmp should be preserved for
- interactive, user-to-user communication. A separate file should record
- all sessions.
- X
- lastlog can be improved in many ways, which we will not discuss in
- detail here. Various vendors (e.g., DEC) have already added features
- along these lines.
- X
- X
- X4. pty user login files
- X
- The author's pseudo-tty session manager, pty 4.0 ([]), maintains several
- user login files. utmp and wtmp record interactive sessions as above.
- The user can specify at session startup whether the session is
- interactive or not. For maximum flexibility, pty lets the user choose
- his host field in utmp. This way the user can configure his sessions to
- work properly with various utmp-processing programs. (The system
- administrator may disable these features.)
- X
- All disconnectable sessions are recorded in sessnow, which has fields
- for username, uid, start/stop time, session master process id, and line.
- sesslog keeps a permanent record of changes to sessnow. Lines are
- specified by two characters (e.g., p0 for /dev/ttyp0).
- X
- The relation between sessions and connections is recorded in scnow.
- scnow lists each session by line, the latest connection start/stop time,
- and complete remote host information. All changes to scnow are listed in
- sclog.
- X
- Note that it makes sense to record connections separately, even those
- not connected to any particular session. The connection managers (getty,
- telnetd, etc.) might keep connnow and connlog files listing current
- connections. In the meantime pty maintains sessnow, sesslog, scnow,
- sclog, utmp, and wtmp. This finally makes a logical set of records for
- which user was using what session from where.
- X
- Notice the clean distinction between connection information and session
- information. All session information is maintained by one program, pty.
- The utmp handling can be completely removed from init, getty, telnetd,
- login, rlogind, screen, xterm, sunview, and dozens of other programs.
- X
- X
- X5. Security issues
- X
- It is worth noting that Sun destroyed any credibility it might have had
- in its user login files by making /etc/utmp mode 666. This is what the
- author calls a SCINUP: Security Compromise Introduced in the Name of
- User Power. Sun found so many programs in its toolset that wanted to
- update utmp that it removed all utmp protection rather than implement a
- proper security mechanism. Years later, security experts (and system
- crackers) are still finding devastating holes caused by Sun's incredibly
- poor judgment.
- X
- Needless to say, the author does not approve of unprotected login files.
- A single program---pty---can provide safe utmp service for all programs
- which need it.
- X
- X
- References
- X
- XXXX
- END_OF_FILE
- if test 6271 -ne `wc -c <'ULOGS.draft1'`; then
- echo shar: \"'ULOGS.draft1'\" unpacked with wrong size!
- fi
- # end of 'ULOGS.draft1'
- fi
- if test -f 'pty.1' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'pty.1'\"
- else
- echo shar: Extracting \"'pty.1'\" \(7074 characters\)
- sed "s/^X//" >'pty.1' <<'END_OF_FILE'
- X.TH pty 1
- X.SH NAME
- pty \- run a program under a pseudo-terminal session
- X.SH SYNTAX
- pty
- X[
- X\fB\-qQve3EdDjJsStTrR0\fI\fP
- X] [
- X\fB\-h\fIhost\fP
- X] [
- X\fB\-O\fIremote\fP
- X] [
- X\fB\-p[cCdDeEnNrRsS780]\fI\fP
- X] [
- X\fB\-x[cCeEfFrRsSuUwWxX]\fI\fP
- X] [
- X\fB\-ACHUVW\fI\fP
- X]
- program
- X[
- arg ...
- X]
- X.SH DESCRIPTION
- X.B pty
- detaches itself from its original
- terminal,
- allocates a new pseudo-terminal session,
- and runs
- X.I program
- on that pseudo-terminal
- with any given arguments.
- X.B pty
- lets you easily disconnect from and reconnect to
- sessions;
- it has over fifty options for precise control,
- and is meant to act as the sole interface
- between pseudo-terminals and the rest of the system.
- X
- X.B pty
- transmits I/O and job control transparently,
- so that
- X(for instance)
- X.B pty vi
- appears to the user to be the same as
- X.B vi,
- but in fact
- X.B pty
- is much more flexible.
- X.B pty vi
- can be redirected, for example,
- while a simple
- X.B vi
- cannot.
- X.B pty
- has several other uses, as described below.
- X
- There are a few very common invocations of
- X.B pty.
- The most common is just
- X.B pty \fIprogram,
- with no options.
- As described in the
- X.B pty-basic
- man page,
- this can be redirected or placed inside a pipeline,
- and
- X.I program
- will behave as if it hadn't been redirected at all.
- X.B pty \-s \fIprogram
- sets up a disconnectable session,
- as described further in
- the
- X.B sess(1)
- man page.
- A disconnectable session will survive modem hangups,
- network glitches, etc. It also
- lets the user disconnect his session at work
- and reconnect later at home without killing
- his jobs in progress.
- X.B pty \-0 \fIprogram
- isolates the rest of the world from
- X.I program
- in several ways;
- it is described further in
- the
- X.B condom(1)
- man page.
- X
- The two next most commonly used options
- are
- X.B\-d,
- if
- X.B pty
- is started without a controlling terminal;
- and
- X.B\-xu,
- which makes an entry in
- X/etc/utmp.
- X.B pty \-d
- is especially useful inside
- X.B rsh,
- where interactive programs normally fail for lack of a tty.
- X
- As noted above,
- some programs (such as
- X.B vi)
- don't like taking input or output
- from a pipe. Under
- X.B pty,
- they won't notice a thing.
- Other programs,
- based on the standard I/O library,
- buffer as much output as possible
- if they're in a pipe.
- Under
- X.B pty,
- they'll buffer only a line of output as usual.
- X.B pty
- is very careful to restore terminal modes upon
- stopping or exiting;
- a careless
- X.I program
- is shielded from your terminal.
- Otherwise,
- X.B pty \fIprogram
- X``feels'' just like
- X.I program.
- X
- X.B pty
- sets file descriptor 0 to input from the
- pseudo-terminal, 1 and 2 to output.
- X.B pty
- also supports the ``3 is /dev/tty'' convention:
- it sets up file descriptor 3 for input from, output to,
- and terminal commands for
- X/dev/tty
- X(not the original tty, but the pseudo tty).
- X
- X.B pty
- has many options,
- as documented in the
- X.B pty-opts
- man page.
- X.SH DIAGNOSTICS
- X.TP
- various usage messages
- XExit 1.
- X.TP
- X.I cannot find controlling tty; try -d?
- X.B pty
- is unable to find its current terminal to copy tty modes from.
- X(This version of
- X.B pty
- defines its controlling tty to be the first one of
- the following which is a tty device:
- descriptor 3, /dev/tty, descriptor 0, descriptor 1, descriptor 2.)
- You probably want to use
- X.B\-d
- to tell
- X.B pty
- to make a pseudo-terminal from scratch.
- XExit 2.
- X.TP
- X.I cannot get modes of original tty
- This shouldn't happen.
- XExit 3.
- X.TP
- X.I cannot set modes of original tty
- This shouldn't happen.
- XExit 4.
- X.TP
- X.I no ptys available
- Self-explanatory.
- XExit 5.
- X.TP
- X.I cannot set up open descriptors
- There is a serious problem with your pseudo-terminal setup.
- Report this error to your system administrator.
- XExit 6.
- X.TP
- X.I cannot fork
- X.I pty
- has run out of processes.
- XExit 7.
- X.TP
- X.I signaller cannot change to session directory
- Probably the session directory has not been created with
- the appropriate modes, or
- X.B pty
- is not running with sufficient privilege.
- Note that this error is often duplicated or triplicated,
- as the various pieces of
- X.B pty
- depend on the session directory for communication,
- including communication of this error!
- XExit 8.
- X.TP
- X.I out of memory
- Self-explanatory.
- XExit 9.
- X.TP
- X.I cannot create internal pipe
- X.TP
- X.I cannot setsid
- These errors will not happen in my lifetime.
- If they do, it's probably because of quantum bit flips.
- XExit 10.
- X.TP
- X.I signaller having trouble
- X.TP
- X.I signaller cannot read success code from master
- Something has gone very wrong with
- the communications paths inside
- X.B pty.
- This should not happen.
- XExit 11.
- X.TP
- X.I session already connected somewhere else
- This error can happen upon session reconnect
- and, in that case, is self-explanatory.
- XExit 16.
- X.TP
- X.I cannot find session; does it exist?
- Self-explanatory, again upon session reconnect.
- XExit 17.
- X.TP
- various \fIcannot\fR messages
- If something is slightly wrong with the user-supplied environment
- X(for instance,
- if
- X.I program
- does not exist)
- X.B pty
- will print an error but
- will exit 0,
- as if it had successfully executed a shell script
- which then detected the error.
- This is consistent with the way that
- X.B pty
- does not report the exit status of
- X.I program.
- X.TP
- X.I warning: cannot write utmp entry
- Self-explanatory.
- X.TP
- X.I warning: cannot dissociate from current tty
- X.B pty
- is unable to submit to the kernel's controlling tty demands.
- If, for example, the tty has
- TIOCEXCL mode set, then there is absolutely no reliable
- way for a program to dissociate itself from that tty.
- This may or may not affect
- X.I program.
- X.TP
- X.I warning: no secure ptys available
- X.B pty
- X4.0
- comes with security features which,
- on a standard BSD 4.3-derived system,
- guarantee that any
- pseudo-tty
- used will be completely clean.
- This message means that no clean ptys are available.
- X(There is an option which will force
- X.B pty
- to exit in this case.)
- X.TP
- X.I warning: cannot set exclusive use on pseudo-tty
- Self-explanatory.
- X.SH BUGS
- None known, but they're probably hiding somewhere.
- X.SH "MANDATORY SERMON"
- UNIX programs have traditionally allocated pseudo-terminals
- with the same code copied from one application to the next.
- Maintaining or adding features to the pseudo-terminal system
- is nearly impossible,
- as so many programs depend on its current internals.
- Porting a program to a new system often means
- rewriting its pseudo-terminal allocation code.
- And the reliability and security of the entire system are
- jeapordized by
- this decentralized management
- of one of the UNIX system's most important resources.
- X
- X.B pty
- solves these problems.
- If programmers use
- X.B pty
- instead of writing equivalent code in each program,
- then everything becomes much more portable and bug-free.
- The only program that need be changed for a different
- system with a different pseudo-terminal mechanism
- is
- X.B pty.
- X(This is called ``modularity,''
- X``interface design,''
- or ``outside-in programming.'')
- X.B pty
- is small enough to be reasonbly reliable,
- and it solves the security problems that plague
- BSD pseudo-ttys.
- It is the author's opinion that
- X.B pty
- is the ``right'' interface between pseudo-ttys
- and the rest of the system.
- X.SH VERSION
- pty version 4.0, 2/9/92.
- X.SH AUTHOR
- Copyright 1992, Daniel J. Bernstein.
- X.SH "SEE ALSO"
- pty-basic(1),
- pty-opts(1),
- sess(1),
- condom(1),
- pty(4),
- tty(4)
- END_OF_FILE
- if test 7074 -ne `wc -c <'pty.1'`; then
- echo shar: \"'pty.1'\" unpacked with wrong size!
- fi
- # end of 'pty.1'
- fi
- if test -f 'ptysecure.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'ptysecure.c'\"
- else
- echo shar: Extracting \"'ptysecure.c'\" \(6764 characters\)
- sed "s/^X//" >'ptysecure.c' <<'END_OF_FILE'
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <sys/file.h>
- X#include <errno.h>
- extern int errno;
- X#include "ptysecure.h"
- X#include "ptytty.h"
- X#include "config/ptymodes.h"
- X#include "config/ptygroup.h"
- X
- static struct stat storig;
- X
- X/* must close fdm and fds upon -1 return */
- int ptysecure(fdm,fds,ext,fnm,fns,flagxchown,allowinsecure)
- int *fdm; /* master */
- int *fds; /* slave */
- char *ext; /* char array, not string */
- char *fnm;
- char *fns;
- int flagxchown;
- int allowinsecure;
- X{
- X/* XXX: check that the pathnames are secure? nah */
- X/* XXX: vhangup(): don't make me laugh */
- X/* XXX: revoke() under 4.4 */
- X/* XXX: ofiles... fstat... pff... */
- X/* XXX: opencount()---but what about unprivileged users? */
- X
- X/* We have the master and slave open. */
- X/* Any number of other processes may have the slave open. */
- X/* Any number of other processes may have our pgrp. */
- X
- X/* We depend on never passing the master side to another process. */
- X/* We depend on opens of the master side being mutually exclusive. */
- X/* We opened the master, so no other process has the master open. */
- X/* Through this routine we will never close the master. */
- X/* Hence no other process will have the master open while we work. */
- X
- X char buf[20];
- X int fdp[2];
- X char psarg1[20];
- X int kidpid;
- X int numpsrets;
- X int pspid;
- X int flagchmodworked;
- X int parsingpid;
- X char *(psargs[3]);
- X int r;
- X
- X if (fstat(*fds,&storig) == -1)
- X goto death;
- X flagchmodworked = 1;
- X if (storig.st_uid != geteuid())
- X flagchmodworked = 0;
- X if (!allowinsecure && !flagchmodworked)
- X goto death;
- X if (fchmod(*fds,0600) == -1)
- X {
- X flagchmodworked = 0;
- X /* XXX: warning? other action? */
- X }
- X if (!allowinsecure && !flagchmodworked)
- X goto pdeath;
- X/* All security guarantees are off unless the slave tty is now */
- X/* protected from open() by normal users. In other words, we require */
- X/* either (1) being the owner of the tty, so that the fchmod worked, */
- X/* or (2) being privileged and having the slave ttys be protected */
- X/* all the time anyway. (2) is a better situation. (1) is more */
- X/* realistic for pty installations under current systems. Anyway, */
- X/* we don't do the more powerful security tests if we can't chmod. */
- X
- X/* Anyway, we depend on the slave tty now being unopenable by users. */
- X/* This situation will persist until we change the mode again. */
- X/* (In situation (2) it will always be true.) */
- X
- X/* We depend on read() of the master side returning 0 or -1/EIO if */
- X/* nobody has the slave side open, and something else otherwise. */
- X close(*fds); r = read(*fdm,buf,1);
- X if (r > 0) { /* XXX: warning? */ goto pmdeath; }
- X if ((r == -1) && (errno != EIO) && (errno != EWOULDBLOCK))
- X { /* XXX: warning? */ goto pmdeath; }
- X *fds = open(fns,O_RDWR);
- X if (*fds == -1) goto pmdeath;
- X
- X/*
- Now nobody but us has the slave side open.
- XFurthermore, as noted above, nobody but us has the master side open,
- and nobody will be able to open either the master or the slave.
- X
- But we're still not done! A process can access a tty even if the tty
- is completely protected, and even if it doesn't have a descriptor open
- to the tty. (Does this sound stupid? It is.) All it has to do is open
- X/dev/tty, provided that it has the right controlling terminal. Before
- pronouncing the tty secure we have to take care of processes with the
- same ctty.
- X
- We depend on the fact that all methods of associating a process with a
- tty depend on (1) having the actual tty (not just /dev/tty) open; or
- X(2) opening the tty. We know that nobody can have the actual tty open
- from here on.
- X*/
- X
- X if (flagchmodworked)
- X {
- X if (pipe(fdp) == -1)
- X goto pdeath;
- X
- X switch(kidpid = fork())
- X {
- X case -1:
- X close(fdp[0]);
- X close(fdp[1]);
- X goto pdeath;
- X case 0:
- X /* XXX: WARNING! We invoke /bin/ps with our privileges! */
- X /* If we switched back to the real uid, we couldn't trust the results. */
- X close(*fdm); close(*fds);
- X close(0); if (dup(fdp[0]) != 0) exit(1);
- X close(1); if (dup(fdp[1]) != 1) exit(1);
- X close(2); if (dup(fdp[1]) != 2) exit(1);
- X close(fdp[0]);
- X close(fdp[1]);
- X close(0);
- X psargs[0] = "/bin/ps";
- X psargs[1] = psarg1;
- X psarg1[0] = 'c'; psarg1[1] = 'g'; psarg1[2] = 'a'; psarg1[3] = 'x';
- X psarg1[4] = 't'; psarg1[5] = ext[0]; psarg1[6] = ext[1]; psarg1[7] = 0;
- X psargs[2] = (char *) 0;
- X setreuid(getuid(),getuid()); /* XXX: do we really want this? */
- X execve(psargs[0],psargs,psargs + 2);
- X exit(1);
- X default:
- X close(fdp[1]);
- X }
- X
- X numpsrets = 0;
- X parsingpid = 0;
- X while (read(fdp[0],buf,1) == 1)
- X {
- X if (parsingpid)
- X if ((buf[0] != ' ') && (10 > (unsigned long) (unsigned char) (buf[0] - '0')))
- X pspid = pspid * 10 + (buf[0] - '0');
- X else if (pspid)
- X {
- X parsingpid = 0;
- X /* XXX: tell user about pspid? */
- X if ((pspid == getpid()) || (pspid == kidpid))
- X --numpsrets; /*XXX*/
- X }
- X if (buf[0] == '\n')
- X {
- X parsingpid = 1;
- X pspid = 0;
- X ++numpsrets;
- X }
- X }
- X close(fdp[0]);
- X wait((int *) 0); /*XXX*/
- X
- X if (numpsrets != 1)
- X {
- X /* XXX: warning? */
- X goto pdeath;
- X }
- X }
- X
- X/*
- We depend on /bin/ps being set up so that numpsrets == 1 implies that
- there were, at some point in time, no processes (except possibly us)
- with that tty as controlling tty.
- X
- Now lots of processes could have /dev/tty open and somehow pointing to
- this tty, but there are none (other than us) with the actual tty open,
- or with the master open, or with this controlling tty. There's an easy
- way to finish off: we simply repeat the first test!
- X*/
- X close(*fds); r = read(*fdm,buf,1);
- X if (r > 0) { /* XXX: warning? */ goto pmdeath; }
- X if ((r == -1) && (errno != EIO) && (errno != EWOULDBLOCK))
- X { /* XXX: warning? */ goto pmdeath; }
- X *fds = open(fns,O_RDWR);
- X if (*fds == -1) goto pmdeath;
- X
- X/*
- XFinally! The pseudo-tty is secure---or at least a hell of a lot more
- secure than the ttys you get from any other program.
- X*/
- X
- X if (fchmod(*fds,PTYMODE_USED) == -1)
- X {
- X /* XXX: warning? */
- X ;
- X }
- X if (flagxchown)
- X {
- X if (fchown(*fds,getuid(),PTYGROUP) == -1)
- X {
- X /* XXX: warning? */
- X ;
- X }
- X }
- X return 0;
- X
- pmdeath:
- X *fds = open(fns,O_RDWR);
- pdeath:
- X if (*fds != -1)
- X fchmod(*fds,PTYMODE_UNUSED);
- death:
- X close(*fds); /* *fds could be -1; that's okay */
- mdeath:
- X *fds = open("/dev/tty",O_RDWR);
- X if (*fds != -1)
- X {
- X if (tty_dissoc(*fds) == -1)
- X ;
- X close(*fds);
- X }
- X close(*fdm);
- X return -1;
- X}
- X
- int ptyunsecure(fdm,fds,ext)
- int fdm;
- int fds;
- char *ext;
- X{
- X if (fchmod(fds,PTYMODE_UNUSED) == -1)
- X {
- X /* XXX: warning? */
- X ;
- X }
- X if (getuid() != storig.st_uid)
- X if (fchown(fds,storig.st_uid,PTYGROUP) == -1)
- X {
- X /* XXX: warning? */
- X ;
- X }
- X return 0;
- X}
- END_OF_FILE
- if test 6764 -ne `wc -c <'ptysecure.c'`; then
- echo shar: \"'ptysecure.c'\" unpacked with wrong size!
- fi
- # end of 'ptysecure.c'
- fi
- if test -f 'ptysigler.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'ptysigler.c'\"
- else
- echo shar: Extracting \"'ptysigler.c'\" \(7958 characters\)
- sed "s/^X//" >'ptysigler.c' <<'END_OF_FILE'
- X#include <sys/types.h>
- X#include <sys/wait.h>
- X#include <sys/time.h>
- X#include <sys/resource.h>
- X#include <signal.h>
- X#include "sigsched.h"
- X#include "sigdfl.h"
- X#include "fmt.h"
- X#include "sessconnlog.h"
- X#include "ptytty.h"
- X#include "ptycomm.h"
- X#include "ptymisc.h"
- X#include "ptyerr.h"
- X#include "config/ttyopts.h"
- X#include "ptysigler.h"
- extern void doconnect(); /* XXX: gaargh */
- extern void ckobey();
- X
- X#define verbose 0, /*XXX*/
- X
- static int firsttime;
- X
- static char resp6[6];
- static char *sremote;
- static int sremotelen;
- X
- static int sflagttymodes; /* must be 0 if !sflagreading */
- static struct ttymodes stmotty; /* only valid if sflagttymodes */
- static struct ttymodes stmottyzero; /* only valid if sflagttymodes */
- static int sflagreading;
- static int sflagjobctrl;
- static int fdmaster = -1;
- static int fdus2master = -1;
- X
- static int fdtty = -1;
- X
- static int suid = -1;
- static char recoext[2]; /* if null, then unset */
- X
- void lastmoment(n)
- int n;
- X{
- X /* clean up and die! */
- X if (sflagttymodes)
- X tty_setmodes(fdtty,&stmotty);
- X}
- X
- void stop(sig)
- int sig;
- X{
- X sigdfl(sig);
- X}
- X
- void byebye(sig)
- int sig;
- X{
- X lastmoment(0);
- X sigdfl(sig);
- X die(DIE_IMPOSSIBLE);
- X}
- X
- void obey(n)
- int n;
- X{
- X int r;
- X char c[4];
- X
- X verbose("sigler entering obey");
- X r = bread(fdmaster,c,4);
- X verbose("sigler obeyread %d %c %d %d %d",r,c[0],c[1],c[2],c[3]);
- X
- X if (r <= 0)
- X c[0] = 'd'; /* kludge alert! */
- X
- X switch(c[0])
- X {
- X case 'z': /* slave stopped, c[1] is stop signal */
- X if (sflagjobctrl)
- X {
- X /* XXX: error checks! */
- X if (sflagttymodes)
- X tty_setmodes(fdtty,&stmotty);
- X sigdfl(c[1]); /* my, we are so trusting... */
- X if (sflagreading)
- X tty_forcefg(fdtty);
- X if (sflagttymodes)
- X tty_setmodes(fdtty,&stmottyzero);
- X bwrite(fdus2master,"C",1); /* XXX: error checks? */
- X }
- X break;
- X case 'k': /* sesskill */
- X case 'd': /* disconnect */
- X case 'e': /* slave exited, c[1] is exit status */
- X case 's': /* slave terminated by signal, c[1] is signum, c[2] is coredump */
- X if (recoext[0])
- X doconnect();
- X else
- X {
- X close(fdmaster); fdmaster = -1;
- X close(fdus2master); fdus2master = -1;
- X ckobey();
- X lastmoment(0);
- X }
- X break;
- X case 'r': /* recoext, c[1] and c[2] */
- X recoext[0] = c[1];
- X recoext[1] = c[2];
- X break;
- X }
- X}
- static ss_sig *sigobey = 0;
- void ckobey()
- X{
- X if (!sigobey && (fdmaster != -1))
- X { ss_schedwait(sigobey = ss_sigread(fdmaster),obey,0,1); return; }
- X if (sigobey && (fdmaster == -1))
- X { ss_unsched(sigobey,obey,0); sigobey = 0; }
- X}
- X
- void doconnect()
- X{
- X /* assumptions: */
- X /* we are in the foreground if sflagreading */
- X /* fd 0 is input, fd 1 is output, fd 2 is error---all open */
- X /* tty is in mode smottyzero if sflagttymodes */
- X
- X int fdcomm;
- X int pi[2];
- X int sp[2];
- X
- X if (!firsttime)
- X {
- X char buf[50]; char *t; t = buf;
- X t += fmt_strncpy(t,"reconnecting to ",0);
- X *t++ = recoext[0]; *t++ = recoext[1]; *t = 0;
- X warn("info",buf);
- X }
- X verbose("sigler entering doconnect");
- X if (fdmaster != -1) { close(fdmaster); fdmaster = -1; }
- X if (fdus2master != -1) { close(fdus2master); fdus2master = -1; }
- X
- X if ((pipe(pi) == -1) || (pipe(sp) == -1))
- X {
- X lastmoment(0);
- X if (firsttime) /* hope not */
- X warn("fatal","signaller cannot create internal pipe; master may still be running; use sesslist to check, then try sesskill or reconnect later");
- X else
- X warn("fatal","signaller cannot create internal pipe");
- X die(DIE_IMPOSSIBLE);
- X }
- X
- X fdcomm = comm_write(recoext,suid);
- X if (fdcomm == -1) /* hope this isn't the first connect */
- X {
- X lastmoment(0);
- X if (firsttime)
- X warn("fatal","signaller cannot connect; master may still be running; use sesslist to check, then try sesskill or reconnect later");
- X else
- X {
- X char buf[100]; char *t; t = buf;
- X /* this is a typical case so we print a nice error message */
- X t += fmt_strncpy(t,"signaller cannot reconnect to session ",0);
- X t += fmt_strncpy(t,recoext,2);
- X t += fmt_strncpy(t,"; does it exist?",0);
- X *t = 0;
- X warn("fatal",buf);
- X }
- X die(DIE_EXIST);
- X }
- X verbose("sigler opened connection");
- X if (
- X (bwrite(fdcomm,"r",1) != 1)
- X ||(bread(fdcomm,resp6,6) != 6)
- X ||(!respeq(resp6,"shrtng"))
- X ||(comm_putfd(fdcomm,sp[1]) == -1)
- X ||(comm_putfd(fdcomm,pi[0]) == -1)
- X ||(comm_putfd(fdcomm,0) == -1)
- X ||(comm_putfd(fdcomm,1) == -1)
- X ||(bwrite(fdcomm,(char *) &sflagreading,sizeof(int)) != sizeof(int))
- X ||(bwrite(fdcomm,(char *) &sremotelen,sizeof(int)) != sizeof(int))
- X ||(bwrite(fdcomm,(char *) sremote,sremotelen) != sremotelen)
- X ||(bread(fdcomm,resp6,6) != 6)
- X ||(!respeq(resp6,"phew! "))
- X )
- X {
- X lastmoment(0);
- X if (firsttime)
- X warn("fatal","signaller having trouble; master may still be running; use sesslist to check, then try sesskill or reconnect later");
- X else
- X if (respeq(resp6,"no-go!")) /* master's already connected */
- X {
- X warn("fatal","session already connected somewhere else");
- X die(DIE_ELSE);
- X }
- X else
- X warn("fatal","signaller having trouble");
- X die(DIE_COMM);
- X }
- X close(sp[1]); fdmaster = sp[0];
- X close(pi[0]); fdus2master = pi[1];
- X
- X#ifdef TTY_WINDOWS
- X /* Obviously there's a race here between the master reconnecting */
- X /* and us telling the pty to change sizes. But will the slave */
- X /* ever care? I'm not sure... */
- X kill(getpid(),SIGWINCH); /* oh, what a royal kludge */
- X#endif
- X
- X close(fdcomm);
- X
- X verbose("sigler successful doconnect");
- X if (!firsttime)
- X {
- X char buf[50]; char *t; t = buf;
- X t += fmt_strncpy(t,"successfully connected to ",0);
- X *t++ = recoext[0]; *t++ = recoext[1]; *t = 0;
- X warn("info",buf);
- X }
- X recoext[0] = recoext[1] = 0;
- X firsttime = 0;
- X
- X ckobey();
- X}
- X
- static void sigchld(n)
- int n;
- X{
- X int w;
- X
- X while (wait3(&w,WNOHANG | WUNTRACED,(struct rusage *) 0) > 0)
- X ; /* [yawn] */
- X}
- X
- static void sigwinch(n)
- int n;
- X{
- X#ifdef TTY_WINDOWS
- X struct ttywin twi;
- X struct ttymodes tmo;
- X
- X if (tty_getmodes(fdtty,&tmo) == 0)
- X {
- X tty_modes2win(&tmo,&twi);
- X bwrite(fdus2master,"W",1);
- X bwrite(fdus2master,(char *) &twi,sizeof(twi));
- X /* XXX: error checks? */
- X }
- X#endif
- X ;
- X}
- X
- X/*
- Signal handling:
- X
- TTIN, TTOU: will never happen, as we don't do I/O; default if they do happen
- PIPE: ditto; default if it does happen
- HUP, INT, QUIT: default. we'll die, master will see socket close.
- X XXX: There's a race here to reconnect to the master initially...
- TSTP: default. master won't see it.
- X
- CHLD: could be master, or a child from before we were execed; ignore both
- X XXX: There's a race upon exiting to avoid making zombies...
- WINCH, and after every manual continue: tell master to winch
- X
- when fdmaster != -1 and it's readable: do obey(). This keeps us going.
- A side effect of this strategy is that if we ever rest for a moment
- without a master to obey, we die. How poetic. Should this be called the
- slave process?
- X*/
- X
- void sigler(ext,uid,pid,flagttymodes,tmotty,tmottyzero,flagreading,flagjobctrl,remote)
- char *ext;
- int uid;
- int pid; /* process id of master---not that we care */
- int flagttymodes;
- struct ttymodes *tmotty;
- struct ttymodes *tmottyzero;
- int flagreading;
- int flagjobctrl;
- char *remote;
- X{
- X /* TTOU, TTIN, PIPE are already SIG_DFL */
- X ss_sched(ss_signal(SIGINT),byebye,SIGINT);
- X ss_sched(ss_signal(SIGHUP),byebye,SIGHUP);
- X ss_sched(ss_signal(SIGTSTP),stop,SIGTSTP);
- X ss_sched(ss_signal(SIGQUIT),byebye,SIGQUIT);
- X ss_sched(ss_signal(SIGCHLD),sigchld,0);
- X#ifdef TTY_WINDOWS
- X ss_sched(ss_signal(SIGWINCH),sigwinch,0);
- X#endif
- X
- X suid = uid;
- X
- X sremote = remote;
- X sremotelen = strlen(remote) + 1;
- X if (sremotelen > SESSCONNLOG_REMOTELEN)
- X sremotelen = SESSCONNLOG_REMOTELEN;
- X sflagttymodes = flagttymodes;
- X sflagreading = flagreading;
- X sflagjobctrl = flagjobctrl;
- X tty_copymodes(&stmotty,tmotty);
- X tty_copymodes(&stmottyzero,tmottyzero);
- X
- X if (flagttymodes)
- X fdtty = tty_getctrl();
- X /* XXX: what if it's -1? is this even remotely possible? */
- X
- X firsttime = 1;
- X
- X recoext[0] = ext[0]; recoext[1] = ext[1];
- X doconnect();
- X}
- END_OF_FILE
- if test 7958 -ne `wc -c <'ptysigler.c'`; then
- echo shar: \"'ptysigler.c'\" unpacked with wrong size!
- fi
- # end of 'ptysigler.c'
- fi
- if test -f 'timer.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'timer.c'\"
- else
- echo shar: Extracting \"'timer.c'\" \(7794 characters\)
- sed "s/^X//" >'timer.c' <<'END_OF_FILE'
- X/* timer.c, timer.h: timer libraries
- Daniel J. Bernstein, brnstnd@nyu.edu.
- Depends on sigsched.h, ralloc.h, sod.h.
- Requires BSD (interval timers, PROF and VTALRM signals, gettimeofday, etc.).
- X7/27/91: Baseline. timer 1.0, public domain.
- No known patent problems.
- X
- All signals defined here are thread-lowered signals.
- X
- Note that we use timer_clock instead of struct timeval since
- timer_clock has at least a prayer of being portable.
- This implementation, however, is BSD-only.
- X*/
- X
- X#include <sys/types.h>
- X#include <sys/time.h>
- X#include <sys/times.h>
- X#include <signal.h>
- X#include "sigsched.h"
- X#include "sod.h"
- X#include "timer.h"
- X#include "ralloc.h"
- X#ifndef HZ
- X#define HZ 60 /*XXX*/
- X#endif
- X
- int timer_now(t,result)
- timer_type t;
- timer_clock *result;
- X{
- X struct tms tms;
- X struct timeval tv;
- X
- X switch(t)
- X {
- X case TIMER_REAL:
- X if (gettimeofday(&tv,(struct timezone *) 0) == -1)
- X return -1;
- X result->sec = tv.tv_sec;
- X result->usec = tv.tv_usec;
- X break;
- X case TIMER_VIRTUAL:
- X times(&tms);
- X result->sec = tms.tms_utime / HZ;
- X result->usec = ((tms.tms_utime % HZ) * 1000000) / HZ;
- X break;
- X case TIMER_PROF:
- X times(&tms);
- X result->sec = (tms.tms_utime + tms.tms_stime) / HZ;
- X result->usec = (((tms.tms_utime + tms.tms_stime) % HZ) * 1000000) / HZ;
- X break;
- X default:
- X return -1;
- X }
- X return 0;
- X}
- X
- void timer_sum(one,two,result)
- timer_clock *one;
- timer_clock *two;
- timer_clock *result;
- X{
- X result->sec = one->sec + two->sec;
- X if ((result->usec = one->usec + two->usec) >= 1000000)
- X {
- X result->sec += 1;
- X result->usec -= 1000000;
- X }
- X}
- X
- int timer_diff(one,two,result)
- timer_clock *one;
- timer_clock *two;
- timer_clock *result;
- X{
- X if (one->sec > two->sec)
- X {
- X result->sec = one->sec - two->sec;
- X if (one->usec >= two->usec)
- X result->usec = one->usec - two->usec;
- X else
- X { --result->sec; result->usec = 1000000 - (two->usec - one->usec); }
- X return 1;
- X }
- X if (one->sec < two->sec)
- X {
- X result->sec = two->sec - one->sec;
- X if (two->usec >= one->usec)
- X result->usec = two->usec - one->usec;
- X else
- X { --result->sec; result->usec = 1000000 - (one->usec - two->usec); }
- X return -1;
- X }
- X if (one->usec > two->usec)
- X {
- X result->sec = 0;
- X result->usec = one->usec - two->usec;
- X return 1;
- X }
- X if (one->usec < two->usec)
- X {
- X result->sec = 0;
- X result->usec = two->usec - one->usec;
- X return 1;
- X }
- X result->sec = 0;
- X result->usec = 0;
- X return 0;
- X}
- X
- X/* Basic idea: For each kind of timer, keep a list of all scheduled */
- X/* events. Set the interval timers to go off at the first events. */
- X
- struct kaboom { timer_clock when; ss_thread *t; int flagi; ss_id i; ss_idptr p; int wait; } ;
- X
- SODdecl(kaboomlist,struct kaboom);
- X
- X/* XXX: should use priority queues here */
- X
- static int numwait = 0;
- X
- X/* XXX: all these will have to change if TIMER_NUM changes */
- static kaboomlist thead[TIMER_NUM] = { 0, 0, 0 };
- static int tgoing[TIMER_NUM] = { 0, 0, 0 };
- static timer_clock twhen[TIMER_NUM];
- static int t2sig[TIMER_NUM] = { SIGALRM, SIGVTALRM, SIGPROF };
- static int t2it[TIMER_NUM] = { ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF };
- X
- static void kaboomcleanup()
- X{
- X kaboomlist newhead;
- X kaboomlist k;
- X timer_type i;
- X
- X for (i = 0;i < TIMER_NUM;++i)
- X {
- X newhead = 0;
- X while (thead[i])
- X {
- X SODpop(thead[i],k);
- X if (SODdata(k).t) SODpush(newhead,k); else SODfree(k,rfree);
- X }
- X thead[i] = newhead;
- X }
- X}
- X
- static void kaboom(t)
- timer_type t;
- X{
- X kaboomlist k;
- X timer_clock dummy;
- X ss_thread *thread;
- X
- X ss_unsched(ss_signal(t2sig[t]),kaboom,t);
- X tgoing[t] = 0; /* timer's out */
- X for (k = thead[t];k;k = SODnext(k))
- X if (timer_diff(&(SODdata(k).when),twhen + t,&dummy) <= 0)
- X {
- X thread = SODdata(k).t; SODdata(k).t = 0;
- X if (SODdata(k).wait) --numwait; SODdata(k).wait = 0;
- X if (thread)
- X if (SODdata(k).flagi)
- X thread(SODdata(k).i);
- X else
- X thread(SODdata(k).p);
- X /* k may now be invalid. alternative: ss_schedonce(ss_asap(),...) */
- X break; /* important! */
- X }
- X kaboomcleanup();
- X if (kaboomresched(t) == -1)
- X ; /* XXXXXX: uh-oh */
- X}
- X
- static int set_it(t,when)
- timer_type t;
- timer_clock *when;
- X{
- X struct itimerval it;
- X timer_clock now;
- X timer_clock diff;
- X
- X if (timer_now(t,&now) == -1)
- X return -1;
- X if (timer_diff(when,&now,&diff) <= 0)
- X {
- X diff.sec = 0;
- X diff.usec = 1;
- X }
- X it.it_value.tv_sec = diff.sec; it.it_interval.tv_sec = 0;
- X it.it_value.tv_usec = diff.usec; it.it_interval.tv_usec = 0;
- X if (setitimer(t2it[t],&it,(struct itimerval *) 0) == -1)
- X return -1;
- X return 0;
- X}
- X
- static int kaboomresched(t) /* XXX: implicit-static */
- timer_type t;
- X{
- X timer_clock dummy;
- X kaboomlist k;
- X int resched;
- X
- X if (thead[t])
- X {
- X resched = 0;
- X for (k = thead[t];k;k = SODnext(k))
- X if (SODdata(k).t)
- X if (!tgoing[t] || timer_diff(&SODdata(k).when,twhen + t,&dummy) < 0)
- X {
- X resched = 1;
- X twhen[t] = SODdata(k).when; /*XXX: structure copying*/
- X }
- X if (resched)
- X {
- X if (tgoing[t])
- X ss_unsched(ss_signal(t2sig[t]),kaboom,t);
- X tgoing[t] = 1;
- X if (ss_schedwait(ss_signal(t2sig[t]),kaboom,t,numwait) == -1)
- X return -1; /*XXX*/
- X if (set_it(t,twhen + t) == -1)
- X return -1; /*XXX*/
- X }
- X }
- X return 0;
- X}
- X
- static int timer_sched(x,t,flagi,i,p,wait)
- ss_extern *x;
- ss_thread *t;
- int flagi;
- ss_id i;
- ss_idptr p;
- int wait;
- X{
- X timer_sig *tsig;
- X kaboomlist k;
- X int resched;
- X timer_clock dummy;
- X
- X k = SODalloc(kaboomlist,k,ralloc);
- X if (!k)
- X return -1;
- X tsig = (timer_sig *) x->u.c;
- X SODdata(k).when.sec = tsig->when.sec;
- X SODdata(k).when.usec = tsig->when.usec;
- X SODdata(k).t = t;
- X SODdata(k).flagi = flagi;
- X SODdata(k).i = i;
- X SODdata(k).p = p;
- X SODdata(k).wait = wait;
- X
- X resched = 0;
- X if (wait)
- X if (!numwait++)
- X resched = 1;
- X SODpush(thead[tsig->t],k);
- X if (!tgoing[tsig->t] || (timer_diff(&(SODdata(k).when),twhen + tsig->t,&dummy) < 0))
- X resched |= 2;
- X if (resched)
- X {
- X if (tgoing[tsig->t])
- X ss_unsched(ss_signal(t2sig[tsig->t]),kaboom,tsig->t);
- X if (ss_schedwait(ss_signal(t2sig[tsig->t]),kaboom,tsig->t,numwait) == -1)
- X return -1;
- X tgoing[tsig->t] = 1;
- X if (resched & 2) twhen[tsig->t] = SODdata(k).when; /*XXX: struct copying*/
- X if (set_it(tsig->t,twhen + tsig->t) == -1)
- X return -1;
- X }
- X return 0;
- X}
- X
- static int timer_unsched(x,t,flagi,i,p)
- ss_extern *x;
- ss_thread *t;
- int flagi;
- ss_id i;
- ss_idptr p;
- X{
- X timer_sig *tsig;
- X kaboomlist k;
- X struct kaboom *sk;
- X
- X tsig = (timer_sig *) x->u.c;
- X for (k = thead[tsig->t];k;k = SODnext(k))
- X {
- X sk = &(SODdata(k));
- X if ((sk->t == t) && (sk->flagi == flagi) && (sk->i == i) && (sk->p == p))
- X if ((sk->when.usec == tsig->when.usec) && (sk->when.sec == tsig->when.usec))
- X {
- X sk->t = 0;
- X if (sk->wait)
- X --numwait;
- X sk->wait = 0;
- X }
- X }
- X kaboomcleanup();
- X if (kaboomresched(tsig->t) == -1)
- X return -1;
- X return 0;
- X}
- X
- void timer_setsig(tsig,t,when)
- timer_sig *tsig;
- timer_type t;
- timer_clock *when;
- X{
- X tsig->x.sched = timer_sched;
- X tsig->x.unsched = timer_unsched;
- X tsig->x.u.c = (char *) tsig; /* my, aren't we the clever ones today */
- X tsig->t = t;
- X tsig->when.sec = when->sec;
- X tsig->when.usec = when->usec;
- X ss_externsetsig(&(tsig->sig),&(tsig->x));
- X}
- X
- static int init = 0;
- X
- int timer_init()
- X{
- X struct itimerval it;
- X it.it_value.tv_sec = 0; it.it_value.tv_usec = 0;
- X it.it_interval.tv_sec = 0; it.it_interval.tv_usec = 0;
- X if (init)
- X return 0;
- X init = 1;
- X if (ss_addsig(SIGALRM) == -1)
- X return -1;
- X if (setitimer(ITIMER_REAL,&it,(struct itimerval *) 0) == -1)
- X return -1;
- X if (ss_addsig(SIGPROF) == -1)
- X return -1;
- X if (setitimer(ITIMER_PROF,&it,(struct itimerval *) 0) == -1)
- X return -1;
- X if (ss_addsig(SIGVTALRM) == -1)
- X return -1;
- X if (setitimer(ITIMER_VIRTUAL,&it,(struct itimerval *) 0) == -1)
- X return -1;
- X /* we may end up receiving up to one of each signal, but that's okay */
- X return 0;
- X}
- END_OF_FILE
- if test 7794 -ne `wc -c <'timer.c'`; then
- echo shar: \"'timer.c'\" unpacked with wrong size!
- fi
- # end of 'timer.c'
- fi
- echo shar: End of archive 5 \(of 9\).
- cp /dev/null ark5isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 9 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-